/*
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.
 */
package erwiki.plugins;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

import org.apache.commons.lang3.math.NumberUtils;
import org.elwiki.permissions.PermissionFactory;
import org.elwiki_data.WikiPage;
import org.osgi.framework.FrameworkUtil;

import erwiki.api.WikiPartReference;
import erwiki.api.auth.AuthorizationManager;
import erwiki.api.core.WikiContext;
import erwiki.api.exceptions.PluginException;
import erwiki.api.exceptions.ProviderException;
import erwiki.api.pages.PageManager;
import erwiki.api.plugin.PluginManager;
import erwiki.api.plugin.WikiPlugin;
import erwiki.api.preferences.Preferences;
import erwiki.api.render.RenderingManager;
import erwiki.util.HttpUtil;
import erwiki.util.TextUtil;

/**
 * Плагин ErWiki. Вставляет содержимое страницы.
 * <p>
 * Параметры плагина:
 * <ul>
 * <li><b>page</b> - имя вставляемой страницы.</li>
 * <li><b>style</b> - используемый стиль.</li>
 * <li><b>maxlength</b> - максимальная длина вставляемой страницы (содержимое страницы).</li>
 * <li><b>class</b> - используемый класс.</li>
 * <li><b>section</b> - раздел страницы, который необходимо вставить (отделенный знаком
 * "----").</li>
 * <li><b>default</b> - текст для вставки, если запрошенная страница не существует.</li>
 * </ul>
 *
 * @author Scott Hurlbert, Muchos (Muchos thanks to Scott Hurlbert for the initial code.)
 */
public class InsertPagePlugin implements WikiPlugin {

	/** Имя параметра для настройки <i>page ID</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_PAGEID = "id";

	/** Имя параметра для настройки <i>style</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_STYLE = "style";

	/** Имя параметра для настройки <i>maxlength</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_MAXLENGTH = "maxlength";

	/** Имя параметра для настройки <i>class</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_CLASS = "class";

	/** Имя параметра для настройки <i>show option</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_SHOW = "show";

	/** Имя параметра для настройки <i>section</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_SECTION = "section";

	/** Имя параметра для настройки <i>default</i>. Значение <tt>{@value}</tt>. */
	public static final String PARAM_DEFAULT = "default";

	private static final String DEFAULT_STYLE = "";

	private static final String ONCE_COOKIE = "JSPWiki.Once.";

	/**
	 * Этот атрибут спрятан в WikiContext, чтобы гарантировать отсутствие циклических ссылок.
	 */
	public static final String ATTR_RECURSE = "org.apache.wiki.plugin.InsertPage.recurseCheck";

	@WikiPartReference
	private AuthorizationManager authorizationManager;

	@WikiPartReference
	private PageManager pageManager;

	@WikiPartReference
	private RenderingManager renderingManager;

	/** {@inheritDoc} */
	@Override
	public String execute(WikiContext context, Map<String, String> params) throws PluginException {
		StringBuilder res = new StringBuilder();

		/* Разбор параметров.
		 */
		String clazz = params.get(PARAM_CLASS);
		String includedPageId = params.get(PARAM_PAGEID);
		String style = params.get(PARAM_STYLE);
		boolean showOnce = "once".equals(params.get(PARAM_SHOW));
		String defaultstr = params.get(PARAM_DEFAULT);
		int section = NumberUtils.toInt(params.get(PARAM_SECTION), -1);
		int maxlen = NumberUtils.toInt(params.get(PARAM_MAXLENGTH), -1);

		PluginManager pluginManager = context.getEngine().getManager(PluginManager.class);
		ResourceBundle rb = pluginManager.getBundle( //
				Preferences.getLocale(), FrameworkUtil.getBundle(this.getClass()));

		if (style == null) {
			style = DEFAULT_STYLE;
		}

		if (maxlen == -1) {
			maxlen = Integer.MAX_VALUE;
		}

		if (includedPageId != null) {
			WikiPage page;
			try {
				page = pageManager.getPageById(includedPageId);
			} catch (ProviderException e) {
				res.append("<span class=\"error\">Page could not be found by the page provider.</span>");
				return res.toString();
			}

			if (page != null) {
				// Проверка возможной рекурсии.
				List<String> previousIncludes = context.getVariable(ATTR_RECURSE);

				if (previousIncludes != null) {
					if (previousIncludes.contains(page.getName())) {
						return "<span class=\"error\">Error: Circular reference - you can't include a page in itself!</span>";
					}
				} else {
					previousIncludes = new ArrayList<>();
				}

				// Проверка разрешения доступа.
				if (!authorizationManager.checkPermission(context.getWikiSession(),
						PermissionFactory.getPagePermission(page, "view"))) {
					res.append("<span class=\"error\">You do not have permission to view this included page.</span>");
					return res.toString();
				}

				// Показать один раз.
				// Проверка наличия cookie страницы, включать страницу только в случае, если cookie еще не
				// установлен.
				String cookieName = "";

				if (showOnce) {
					cookieName = ONCE_COOKIE + TextUtil.urlEncodeUTF8(page.getName()).replaceAll("\\+", "%20");

					if (HttpUtil.retrieveCookieValue(context.getHttpRequest(), cookieName) != null) {
						return ""; // тихий выход.
					}
				}

				// перейти сюда, после преждевременных точек выхода (permissions, page-cookie)
				previousIncludes.add(page.getName());
				context.setVariable(ATTR_RECURSE, previousIncludes);

				/**
				 * Мы хотим, чтобы вставка происходила в контексте отдельной страницы, поскольку нам нужны
				 * правильные ссылки.
				 */
				WikiContext includedContext = null; //TODO::FVK: context.clone();
				includedContext.setPage(page);

				String pageData = page.getContentText(context.getPageVersion());
				String moreLink = "";

				if (section != -1) {
					try {
						pageData = TextUtil.getSection(pageData, section);
					} catch (IllegalArgumentException e) {
						throw new PluginException(e.getMessage());
					}
				}

				if (pageData.length() > maxlen) {
					pageData = pageData.substring(0, maxlen) + " ...";
					moreLink = "<p><a href=\"" + context.getURL(WikiContext.PAGE_VIEW, includedPageId) + "\">"
							+ rb.getString("insertpage.more") + "</a></p>";
				}

				res.append("<div class=\"inserted-page ");
				if (clazz != null)
					res.append(clazz);
				if (!style.equals(DEFAULT_STYLE))
					res.append("\" style=\"" + style);
				if (showOnce)
					res.append("\" data-once=\"" + cookieName);
				res.append("\" >");

				res.append(renderingManager.textToHTML(pageData));
				res.append(moreLink);

				res.append("</div>");

				//
				// Удалить имя из стека; мы закончили с этим.
				//
				previousIncludes.remove(page.getName());
				context.setVariable(ATTR_RECURSE, previousIncludes);
			} else {
				if (defaultstr != null) {
					res.append(defaultstr);
				} else {
					res.append("There is no page called '" + includedPageId + "'.  Would you like to ");
					res.append("<a href=\"" //
							+ context.getURL(WikiContext.PAGE_EDIT, includedPageId) + "\">create it?</a>");
				}
			}
		} else {
			res.append("<span class=\"error\">");
			res.append("You have to define a page!");
			res.append("</span>");
		}
		return res.toString();
	}

}
